use criterion::{criterion_group, BenchmarkId, Criterion};

use super::runner::runner_from_query;

/// Benchmarks measuring performance of partial queries.

/// This benchmark creates a policy that results in a partial with many AND'd
/// dot lookups. The performance of this benchmark over different input sizes
/// effectively captures how quickly we can:
/// - add constraints
/// - simplify conjunctions of many constraints
///
/// A run of this benchmark "partial_and/5" effectively times how long it takes to
/// add 5 field comparisons to a partial.
pub fn partial_and(c: &mut Criterion) {
    let mut group = c.benchmark_group("partial_and");
    for n in &[1, 5, 10, 20, 40, 80, 100] {
        group.bench_function(BenchmarkId::from_parameter(format!("{}", n)), |b| {
            b.iter_batched_ref(
                || {
                    let runner = runner_from_query("partial_and(r)");
                    let lookups = (0..*n)
                        .map(|n| format!("r.foo_{n} = {n}", n = n))
                        .collect::<Vec<String>>()
                        .join(" and ");

                    let policy = format!(r#"partial_and(r) if {lookups};"#, lookups = lookups);

                    runner.load_str(&policy).unwrap();
                    runner
                },
                |runner| {
                    runner.run();
                },
                criterion::BatchSize::SmallInput,
            )
        });
    }

    group.finish();
}

/// This benchmark creates a policy that results in partials with multiple rule
/// depths. This tests the ability of the simplifier to resolve long chains of temporary
/// variables (generated by rule calls)
///
/// Makes a policy like:
///
/// a(r, 5) if
///     r.foo_5 = 5 and
///     a(r, 4);
///
/// a(r, 4) if r.foo_4 = 4 and a(r, 3);
///
/// ...
///
/// a(r, 0) if r.foo_0 = 0;
///
/// A run of this benchmark parital_rule_depth/5 times how long it takes to run
/// a partial that calls 5 rules and does 5 field comparisons.
pub fn partial_rule_depth(c: &mut Criterion) {
    let mut group = c.benchmark_group("partial_rule_depth");
    for n in &[1, 5, 10, 20, 40, 80, 100] {
        group.bench_function(BenchmarkId::from_parameter(format!("{}", n)), |b| {
            b.iter_batched_ref(
                || {
                    let runner = runner_from_query("partial_rule_depth(r)");
                    let rules = (1..(n + 1))
                        .map(|n| {
                            format!(
                                r#"
                            a(r, {n}) if
                                r.foo_{n} = {n} and
                                a(r, {n1});
                        "#,
                                n = n,
                                n1 = n - 1
                            )
                        })
                        .collect::<Vec<String>>()
                        .join("\n");

                    let policy = format!(
                        r#"
                        partial_rule_depth(r) if a(r, {n});

                        {rules}

                        a(r, 0) if r.foo_0 = 0;
                        "#,
                        rules = rules,
                        n = n
                    );

                    runner.load_str(&policy).unwrap();
                    runner
                },
                |runner| {
                    runner.run();
                },
                criterion::BatchSize::SmallInput,
            )
        });
    }

    group.finish();
}

criterion_group!(benches, partial_and, partial_rule_depth);
